#include <algorithm>
#include "stringtool.h"
#include "print.h"
#include "chartool.h"

template <typename... Args>
void print_error(Args &&...args)
{
    chainsaw::print_class_error("stringtool", std::forward<Args>(args)...);
}

bool chainsaw::stringtool::is_index_valid(int index) const
{
    if (!size())
        return false;
    if (index >= 0 && index < size())
    {
        return true;
    }
    return false;
}

template <typename T>
int find(std::string m_data, T t)
{
    int pos = static_cast<int>(m_data.find(t));
    return pos == std::string::npos ? -1 : pos;
}

int chainsaw::stringtool::find(char c) const
{
    return ::find(m_data, c);
}

int chainsaw::stringtool::find(std::string const &s) const
{
    return ::find(m_data, s);
}

std::vector<int> chainsaw::stringtool::find_all(char c) const
{
    std::vector<int> res;
    for (int i = 0; i < size(); ++i)
    {
        if (c == m_data[i])
            res.push_back(i);
    }
    return res;
}

std::string chainsaw::stringtool::substr(int beginpos, int endpos) const
{
    if (!is_index_valid(beginpos) || !is_index_valid(endpos - 1))
        return m_data;

    std::string result = "";

    if (endpos > beginpos)
    {
        result = m_data.substr(beginpos, endpos - beginpos);
    }
    else
    {
        // update
        print_error("end-position < begin-position");
        result = m_data;
    }
    return result;
}

std::string chainsaw::stringtool::split(int pos, split_part part) const
{
    std::string result = "";

    if (pos >= size())
    {
        print_error("position > m_data.size()");
    }

    switch (part)
    {
    case split_part::LEFT:
        result = substr(0, pos);
        break;
    case split_part::RIGHT:
        result = substr(pos + 1, size());
        break;
    default:
        print_error("invalid-num");
        break;
    }
    return result;
}

std::vector<std::string> chainsaw::stringtool::split(std::vector<int> pos) const
{
    std::vector<std::string> res;

    if (pos.empty())
        return {m_data};

    int last = -1;
    for (int i : pos)
    {
        res.push_back(substr(last + 1, i));
        last = i;
    }

    res.push_back(substr(last + 1, size()));

    return res;
}

std::string chainsaw::stringtool::replaced(int pos, char value) const
{
    std::string res = m_data;
    if (pos < size() && pos >= 0)
    {
        res[pos] = value;
    }
    return res;
}

std::string chainsaw::stringtool::replace(int pos, char value)
{
    if (pos < size())
    {
        m_data[pos] = value;
    }
    return m_data;
}

std::vector<std::string> chainsaw::stringtool::splitWord() const
{
    std::vector<std::string> res;

    std::string buff = "";

    int n = size();
    for (int i = 0; i < n; ++i)
    {
        char c = m_data[i];
        if (is_word_char(c))
        {
            buff += c;
        }

        if (!is_word_char(c) && !buff.empty() || i == n - 1)
        {
            res.push_back(buff);
            buff.clear();
        }
    }

    return res;
}

std::string chainsaw::to_upper(std::string str)
{
    std::string result = "";
    for (char c : str)
    {
        result += chainsaw::to_upper(c);
    }
    return result;
}

std::string chainsaw::to_lower(std::string str)
{
    std::string result = "";
    for (char c : str)
    {
        result += chainsaw::to_lower(c);
    }
    return result;
}

void chainsaw::trim(std::vector<std::string> &m_data)
{
    for (auto &t : m_data)
    {
        if (!t.empty() && (t.front() == ' ' || t.back() == ' '))
        {
            t = stringtool(t).trim();
        }
    }
}

std::string chainsaw::stringtool::trimmed() const
{
    if (!is_index_valid(0))
        return m_data;
    if (m_data[0] == ' ' || m_data.back() == ' ')
    {
        int pos1 = static_cast<int>(m_data.find_first_not_of(' '));
        int pos2 = static_cast<int>(m_data.find_last_not_of(' '));

        return substr(pos1, pos2 + 1);
    }
    return m_data;
}

std::string chainsaw::stringtool::trim()
{
    m_data = trimmed();
    return m_data;
}

std::string chainsaw::stringtool::left(int n) const
{
    if (!is_index_valid(n - 1))
        return m_data;
    return substr(0, n);
}

std::string chainsaw::stringtool::right(int n) const
{
    if (!is_index_valid(n - 1))
        return m_data;
    return substr(size() - n, size());
}

char chainsaw::stringtool::head() const
{
    if (!is_index_valid(0))
        return 0;
    return m_data[0];
}

char chainsaw::stringtool::tail() const
{
    if (!is_index_valid(0))
        return 0;
    return m_data.back();
}

bool chainsaw::stringtool::startsWith(std::string s) const
{
    return s == left(static_cast<int>(s.size()));
}

bool chainsaw::stringtool::endsWith(std::string s) const
{
    return s == right(static_cast<int>(s.size()));
}

int chainsaw::stringtool::size() const
{
    return static_cast<int>(m_data.size());
}

std::string chainsaw::stringtool::remove_head(int n)
{
    if (!is_index_valid(n - 1))
        return m_data;
    m_data.erase(m_data.begin(), m_data.begin() + n);

    return m_data;
}

std::string chainsaw::stringtool::remove_tail(int n)
{
    if (!is_index_valid(n - 1))
        return m_data;
    m_data.erase(m_data.end() - n, m_data.end());
    return m_data;
}

void chainsaw::stringtool::print() const
{
    message(m_data);
}

std::string chainsaw::stringtool::operator()() const
{
    return m_data;
}

char chainsaw::stringtool::operator[](int index) const
{
    if (!is_index_valid(index))
        return 0;
    return m_data[index];
}

bool chainsaw::stringtool::empty() const
{
    return m_data.empty();
}

bool chainsaw::is_word(std::string const &s)
{
    return std::all_of(s.begin(), s.end(), is_word_char);
}
